#include <iostream>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

const std::string gPathName = "/home/pyu/linux/IPC/Shm";
const int gProj_id = 0x66;
const int gShmSize = 4096;
const int gCreater = 1;
const int gUser = 2;

class Shm
{
private:
    // 将 key 转为十六进制
    std::string KeytoHex(key_t key)
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }
    key_t GetCommKey()
    {
        key_t k = ftok(_pathname.c_str(), _proj_id);
        if (k < 0)
        {
            perror("ftok");
        }
        return k;
    }

    int GetShmHelper(key_t key, int size, int shmflg)
    {
        int shmid = shmget(key, size, shmflg);
        if (shmid < 0)
        {
            perror("shmget");
        }
        return shmid;
    }

    void* AttachShm()
    {
        void* shmadd = shmat(_shmid, nullptr, 0);
        if (shmadd == nullptr)
        {
            perror("shmat");
        }
        std::cout << "who: " << RoleToString(_who) << " attach shm" << std::endl;
        return shmadd;
    }

    void DetachShm(void* shmadd)
    {
        if (shmadd == nullptr)
            return;
        shmdt(shmadd);
        std::cout << "who: " << RoleToString(_who) << " detach shm" << std::endl;
    }

    std::string RoleToString(int who)
    {
        if (who == gCreater)
            return "Creater";
        else if (who == gUser)
            return "gUser";
        else
            return "None";
    }

public:
    Shm(std::string pathname, int proj_id, int who)
        :_pathname(pathname), _proj_id(proj_id), _who(who), _address(nullptr)
    {
        // 获取key
        _key = GetCommKey();

        // 获取共享内存
        if (_who == gCreater)
            GetShmForCreate();
        else if (_who == gUser)
            GetShmForUse();

        _address = AttachShm(); // 挂载
        std::cout << "_key: " << KeytoHex(_key) << std::endl;
        std::cout << "Shmid: " << _shmid << std::endl;
    }

    ~Shm()
    {
        if (_address != nullptr)
            DetachShm(_address); // 共享内存脱离

        if(_who == gCreater)
        {
            int ret = shmctl(_shmid, IPC_RMID, nullptr);
            if (ret < 0)
            {
                perror("shm remove");
            }
            std::cout << "shm remove done..." << std::endl;
        }
    }

    bool GetShmForCreate()
    {
        if (_who == gCreater)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
            if (_shmid < 0)
                return false;
            std::cout << "Shm create done..." << std::endl;
            return true;
        }
        return false;
    }

    bool GetShmForUse()
    {
        if (_who == gUser)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
            if (_shmid < 0)
                return false;
            std::cout << "Shm get done..." << std::endl;
            return true;
        }
        return false;
    }

    void* Addr()
    {
        return _address;
    }
private:
    std::string _pathname; // 生成key
    int _proj_id;

    key_t _key; // 标识符
    int _shmid;

    int _who; // 操作者的身份
    void* _address;
};





void RemoveShm(int shmid)
{
    int ret = shmctl(shmid, IPC_RMID, nullptr);
    std::cout << "Shm remove...done" << std::endl;
}